import type { ReactNode, JSX } from 'react'; import React, { useEffect } from 'react'; import type { GetServerSideProps, GetServerSidePropsContext, } from 'next'; import dynamic from 'next/dynamic'; import Head from 'next/head'; import { ShareLinkLayout } from '~/components/Layout/ShareLinkLayout'; import { DrawioViewerScript } from '~/components/Script/DrawioViewerScript'; import { ShareLinkPageView } from '~/components/ShareLinkPageView'; import type { CommonEachProps } from '~/pages/common-props'; import { NextjsRoutingType, detectNextjsRoutingType } from '~/pages/utils/nextjs-routing-utils'; import { useCustomTitleForPage } from '~/pages/utils/page-title-customization'; import { useIsSearchPage, useIsSharedUser } from '~/states/context'; import { useCurrentPageData, useCurrentPagePath, } from '~/states/page'; import { useHydratePageAtoms } from '~/states/page/hydrate'; import { useDisableLinkSharing, useRendererConfig } from '~/states/server-configurations'; import loggerFactory from '~/utils/logger'; import type { NextPageWithLayout } from '../../_app.page'; import type { GeneralPageInitialProps } from '../../general-page'; import { useInitialCSRFetch } from '../../general-page'; import { registerPageToShowRevisionWithMeta } from '../../general-page/superjson'; import { useHydrateServerConfigurationAtoms } from '../../general-page/use-hydrate-server-configurations'; import { NEXT_JS_ROUTING_PAGE } from './consts'; import { getServerSidePropsForInitial, getServerSidePropsForSameRoute } from './server-side-props'; import type { ShareLinkInitialProps } from './types'; // call superjson custom register registerPageToShowRevisionWithMeta(); const GrowiContextualSubNavigation = dynamic(() => import('~/client/components/Navbar/GrowiContextualSubNavigation'), { ssr: false }); const logger = loggerFactory('growi:next-page:share'); type InitialProps = CommonEachProps & GeneralPageInitialProps & ShareLinkInitialProps; type Props = CommonEachProps | InitialProps; const isInitialProps = (props: Props): props is InitialProps => { return 'isNextjsRoutingTypeInitial' in props && props.isNextjsRoutingTypeInitial; }; const SharedPage: NextPageWithLayout = (props: Props) => { // Initialize Jotai atoms with initial data - must be called unconditionally const shareLink = isInitialProps(props) ? props.shareLink : undefined; const isExpired = isInitialProps(props) ? props.isExpired : undefined; const pageData = isInitialProps(props) ? props.pageWithMeta?.data : undefined; useHydratePageAtoms(pageData, { shareLinkId: shareLink?._id, }); const [currentPage] = useCurrentPageData(); const [currentPagePath] = useCurrentPagePath(); const [rendererConfig] = useRendererConfig(); const [, setIsSharedUser] = useIsSharedUser(); const [, setIsSearchPage] = useIsSearchPage(); const [isLinkSharingDisabled] = useDisableLinkSharing(); // Use custom hooks for navigation and routing // useSameRouteNavigation(); // If initial props and skipSSR, fetch page data on client-side useInitialCSRFetch(isInitialProps(props) && props.skipSSR); // Initialize atom values useEffect(() => { setIsSharedUser(true); setIsSearchPage(false); }, [setIsSharedUser, setIsSearchPage]); // If the data on the page changes without router.push, pageWithMeta remains old because getServerSideProps() is not executed // So preferentially take page data from useSWRxCurrentPage const pagePath = currentPagePath ?? props.currentPathname; const title = useCustomTitleForPage(pagePath); return ( <> {title}
); }; type LayoutProps = Props & { children?: ReactNode } const Layout = ({ children, ...props }: LayoutProps): JSX.Element => { // Hydrate sidebar atoms with server-side data - must be called unconditionally const initialProps = isInitialProps(props) ? props : undefined; useHydrateServerConfigurationAtoms(initialProps?.serverConfig, initialProps?.rendererConfig); return {children}; }; SharedPage.getLayout = function getLayout(page) { return ( <> {page} ); }; // function getAction(props: Props): SupportedActionType { // let action: SupportedActionType; // if (props.isExpired) { // action = SupportedAction.ACTION_SHARE_LINK_EXPIRED_PAGE_VIEW; // } // else if (props.shareLink == null) { // action = SupportedAction.ACTION_SHARE_LINK_NOT_FOUND; // } // else { // action = SupportedAction.ACTION_SHARE_LINK_PAGE_VIEW; // } // return action; // } export const getServerSideProps: GetServerSideProps = async(context: GetServerSidePropsContext) => { // detect Next.js routing type const nextjsRoutingType = detectNextjsRoutingType(context, NEXT_JS_ROUTING_PAGE); if (nextjsRoutingType === NextjsRoutingType.INITIAL) { return getServerSidePropsForInitial(context); } // Lightweight props for same-route navigation return getServerSidePropsForSameRoute(context); }; export default SharedPage;